#pragma once
#include <list>
#include <vector>
#include <string>
#include "yasli/Pointers.h"

struct Tag;

struct TagLocation{
	TagLocation()
	: fileName("")
	, pattern("")
	, line(0)
	{
	}
	const char* fileName;
	const char* pattern;
	int line;
};

enum TagType{
	TAG_CLASS,
	TAG_ENUM,
	TAG_FUNCTION,
	TAG_TYPEDEF,
	TAG_MEMBER,
	TAG_ENUM_VALUE,
	TAG_DEFINE,
	TAG_UNKNOWN,
	TAG_TYPE_COUNT
};

class TagRef{
public:
	TagRef(int index = -1, TagType type = TAG_UNKNOWN)
	: index_(index)
	, type_(type)
	{
	}
	operator bool() const{
		return index_ != -1;
	}
	//inline Tag& operator*() const;
	//inline Tag* operator->() const;
protected:
	int index_;
	TagType type_;
};

class Archive;
struct Tag{
	Tag()
	: name("")
	, nameFull("")
	, title("")
	, titleFull("")
	, signature("")
	, parentClass("")
	, type(TAG_UNKNOWN)
	, prototype(false)
	{
	}
	TagType type;
	
	const char* name;
	const char* nameFull;
	const char* signature;

	const char* title;
	const char* titleFull;
	TagLocation location;
	const char* parentClass;
	bool prototype;

	void serialize(Archive& ar);

	//TagRef parent;
	//typedef std::vector<TagRef> Members;
	//Members members;
    
};

class TagStorage;
class Archive;
class TagSource : public RefCounter{
public:
	TagSource(TagStorage* storage)
	: storage_(storage)
	{}

	virtual ~TagSource() {}
	const char* path() const{ return path_.c_str(); }

	virtual void poll(TagStorage* storage) = 0;
	virtual void setStorage(TagStorage* storage){ storage_ = storage; }
	virtual void reattach() {}

	TagStorage* storage() { return storage_; }

	virtual void serialize(Archive& ar);
protected:
	TagStorage* storage_;
	std::string path_;
};

class TagBlock : public RefCounter{
public:
	TagBlock();
	~TagBlock();

	typedef std::vector<Tag> Tags;

	void clear();
	const Tags& tags(TagType type) const{ return tags_[int(type)]; }
	const char* addConstString(const char* str);
	bool loadFromFile(const char* tagsFileName);
	virtual void serialize(Archive& ar);
	void setPath(const char* fileName);

    void setDead(bool dead){ dead_ = dead; }
    bool dead() const{ return dead_; }
protected:
	typedef std::list<string> Strings;
	Strings strings_;

	Tags tags_[TAG_TYPE_COUNT];
    bool dead_;
	string path_;
};

class TagStorage{
public:
	TagStorage();

	void clear();
	void addSource(TagSource* newSource);

    void addBlock(TagBlock* block);

	typedef std::vector<const Tag*> SortedTags;
	const SortedTags& sortedTags(TagType type) const{ return sortedTags_[int(type)]; }

	bool pollSources();

	typedef std::vector<const Tag*> DeadTags;
	const DeadTags& deadTags(){ return deadTags_; }

	typedef std::vector<const Tag*> NewTags;
	const NewTags& newTags(){ return newTags_; }

	void flushTags(){
		deadTags_.clear();
		newTags_.clear();
		cleared_ = false;
	}

	void serialize(Archive& ar);
	bool cleared() const{ return cleared_; }

	typedef std::vector<SharedPtr< TagSource > > Sources;
	Sources& sources(){ return sources_; }
protected:
	void addTagsByBlock(TagBlock* block);
	int removeTagsByBlock(TagBlock* block);
    typedef vector< SharedPtr<TagBlock> > TagBlocks;
	void pickDeadBlocks(TagBlocks* blocks);

	void sortedInsert(SortedTags& sortedTags, const Tag* tagToInsert);
	SortedTags sortedTags_[TAG_TYPE_COUNT];

    TagBlocks blocks_;
    TagBlocks newBlocks_;

	Sources sources_;
	DeadTags deadTags_;
	NewTags newTags_;
	bool cleared_;    
};

struct TagString{
	TagString(TagBlock& block, const char*& str)
	: block_(block)
	, str_(str)
	{	
	}

	TagBlock& block_;
	const char*& str_;
};
bool serialize(Archive& ar, TagString& tagString, const char* name);

extern TagStorage tagStorage;



inline int compareSymbolName(const char* lhs, const char* rhs)
{
	while(*lhs == '~')
		++lhs;
	while(*rhs == '~')
		++rhs;
	return strcmp(lhs, rhs);
}

struct TagCompare{
	bool operator()(const Tag* lhs, const Tag* rhs){
		if(lhs->type == rhs->type){
			int compareResult = compareSymbolName(lhs->titleFull, rhs->titleFull);
			return (compareResult == 0) ? (lhs->prototype > rhs->prototype) : compareResult < 0;
		}
		else
			return lhs->type < rhs->type;
	}
};

void updateTagsFile(const char* tagsFile, const char* sourceFile);
/*
inline Tag& TagRef::operator*() const{
	return tagStorage.tagByIndex(index_, type_);
}
inline Tag* TagRef::operator->() const{
	return &tagStorage.tagByIndex(index_, type_);
}


*/

